﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace CokAracliKontrol.PathFinding
{
    class Pathfinder
    {
        public const int bigNumber = 10000; // int.MaxValue - 1;
        public const int gridSizeX = 16;
        public const int gridSizeY = 12;

        Point[] _movements;
        CompleteSquare[,] _squares = new CompleteSquare[gridSizeX, gridSizeY];
        
        public CompleteSquare[,] Squares
        {
            get { return _squares; }
            set { _squares = value; }
        }

        public Point getMove(Point p1, Point p2)
        {
            return new Point(p1.X - p2.X, p1.Y - p2.Y);
        }

        public Color GetColor(string colorPattern)
        {
            if (colorPattern != null && colorPattern != "" && colorPattern != "Select...")
            {
                string sred, sblue, sgreen;
                string[] parts;
                int redRangeMin, greenRangeMin, blueRangeMin, redRangeMax, greenRangeMax, blueRangeMax;

                parts = colorPattern.Split('|');

                sred = parts[0];
                sgreen = parts[1];
                sblue = parts[2];

                parts = sred.Split('-');
                redRangeMin = Convert.ToInt32(parts[0]);
                redRangeMax = Convert.ToInt32(parts[1]);

                parts = sgreen.Split('-');
                greenRangeMin = Convert.ToInt32(parts[0]);
                greenRangeMax = Convert.ToInt32(parts[1]);

                parts = sblue.Split('-');
                blueRangeMin = Convert.ToInt32(parts[0]);
                blueRangeMax = Convert.ToInt32(parts[1]);

                int red = (redRangeMin + redRangeMax) / 2;
                int green = (greenRangeMin + greenRangeMax) / 2;
                int blue = (blueRangeMin + blueRangeMax) / 2;

                return Color.FromArgb(red, green, blue);
            }
            else
            {
                return Color.Blue;
            }
        }

        public Pathfinder()
        {
            InitMovements(8);
            ClearSquares();
        }

        public Pathfinder(int movementCount)
        {
            if (movementCount > 4)
            {
                InitMovements(8);
            }
            else
            {
                InitMovements(4);
            }
            ClearSquares();
        }

        public void InitMovements(int movementCount)
        {
            if (movementCount == 4)
            {
                _movements = new Point[]
                {
                    new Point(0, -1),
                    new Point(1, 0),
                    new Point(0, 1),
                    new Point(-1, 0)
                };
            }
            else
            {
                _movements = new Point[]
                {
                    new Point(-1, -1),
                    new Point(0, -1),
                    new Point(1, -1),
                    new Point(1, 0),
                    new Point(1, 1),
                    new Point(0, 1),
                    new Point(-1, 1),
                    new Point(-1, 0)
                };
            }
        }

        public void ClearSquares()
        {
            foreach (Point point in AllSquares())
            {
                _squares[point.X, point.Y] = new CompleteSquare();
            }
        }

        public void ClearLogic()
        {
             foreach (Point point in AllSquares())
            {
                int x = point.X;
                int y = point.Y;
                _squares[x, y].DistanceSteps = Pathfinder.bigNumber;
                _squares[x, y].IsPath = false;
            }
        }

        public Point str2coor(string strCoor)
        {
            string[] strArrayCoor = strCoor.Split(',');

            Point coor = new Point();
            coor.X = Convert.ToInt32(strArrayCoor[0]);
            coor.Y = Convert.ToInt32(strArrayCoor[1]);

            return coor;
        }

        public void DrawBoard(BoardControl boardControl, int vehicleID, ComboGrid.ComboGrid grid, string obstacleColor)
        {
            //for (int othersRow = 0; othersRow < grid.RowsCount; othersRow++)
            //{
            //    if (grid.CellAt(othersRow, "Current Coordinate").Text != "")
            //    {
            //        if (othersRow != vehicleID - 1)
            //        {
            //            Point othersCoor = str2coor(grid.CellAt(othersRow, "Current Coordinate").Text);
            //            _squares[othersCoor.X, othersCoor.Y].ContentCode = SquareContent.Others;
            //        }
            //        else
            //        {
            //            Point othersCoor = str2coor(grid.CellAt(othersRow, "Current Coordinate").Text);
            //            _squares[othersCoor.X, othersCoor.Y].ContentCode = SquareContent.StartLocation(vehicleID);
            //        }
            //    }
            //}

            foreach (Point point in AllSquares())
            {
                int x = point.X;
                int y = point.Y;
                int num = _squares[x, y].DistanceSteps;
                Color setColor = Color.Gainsboro;
                int content = _squares[x, y].ContentCode;

                if (content == SquareContent.FreePath)
                {
                    if (_squares[x, y].IsPath == true)
                    {
                        setColor = Color.Lavender;
                    }
                    else
                    {
                        setColor = Color.White;
                    }
                }
                else
                {
                    if (content == SquareContent.StartLocation(vehicleID))
                    {
                        setColor = GetColor(grid.CellAt(vehicleID - 1, "Vehicle Pattern").Text);
                    }
                    else if (content == SquareContent.FinishLocation(vehicleID))
                    {
                        setColor = Color.LightGreen;
                    }
                    else if (content == SquareContent.Obstacle)
                    {
                        setColor = GetColor(obstacleColor);
                    }
                    else if (content == SquareContent.Others)
                    {
                        setColor = Color.Red;
                    }
                    else
                    {
                        setColor = Color.Green;
                        //content = SquareContent.Others;
                        //_squares[x, y].ContentCode = SquareContent.Others;
                    }
                }
                boardControl.SetHighlight(setColor, x, y);
            }
        }

        public void Pathfind(int vehicleID)
        {
            /*
             * 
             * Find path from vehicle to target. First, get coordinates
             * of vehicle.
             * 
             * */
            Point startingPoint = FindCode(SquareContent.StartLocation(vehicleID));
            int vehiclePositionX = startingPoint.X;
            int vehiclePositionY = startingPoint.Y;
            if (vehiclePositionX == -1 || vehiclePositionY == -1)
            {
                return;
            }
            /*
             * 
             * Vehicle starts at distance of 0.
             * 
             * */
            _squares[vehiclePositionX, vehiclePositionY].DistanceSteps = 0;

            while (true)
            {
                bool madeProgress = false;

                /*
                 * 
                 * Look at each square on the board.
                 * 
                 * */
                foreach (Point mainPoint in AllSquares())
                {
                    int x = mainPoint.X;
                    int y = mainPoint.Y;

                    /*
                     * 
                     * If the square is open, look through valid moves given
                     * the coordinates of that square.
                     * 
                     * */

                    if (SquareOpen(x, y))
                    {
                        int passHere = _squares[x, y].DistanceSteps;

                        foreach (Point movePoint in ValidMoves(x, y))
                        {
                            int newX = movePoint.X;
                            int newY = movePoint.Y;
                            int newPass;

                            if (getMove(mainPoint, movePoint).Equals(_movements[0])
                                || getMove(mainPoint, movePoint).Equals(_movements[2])
                                || getMove(mainPoint, movePoint).Equals(_movements[4])
                                || getMove(mainPoint, movePoint).Equals(_movements[6]))
                            {
                                newPass = passHere + 2;
                            }
                            else
                            {
                                newPass = passHere + 1;
                            }

                            if (_squares[newX, newY].DistanceSteps > newPass)
                            {
                                _squares[newX, newY].DistanceSteps = newPass;
                                madeProgress = true;
                            }
                        }
                    }
                }
                if (!madeProgress)
                {
                    break;
                }
            }
        }

        static private bool ValidCoordinates(int x, int y)
        {
            /*
             * 
             * Our coordinates are constrained between 0 and grid size.
             * 
             * */
            if (x < 0)
            {
                return false;
            }
            if (y < 0)
            {
                return false;
            }
            if (x > Pathfinder.gridSizeX - 1)
            {
                return false;
            }
            if (y > Pathfinder.gridSizeY - 1)
            {
                return false;
            }
            return true;
        }

        private bool SquareOpen(int x, int y)
        {
            /*
             * 
             * A square is open if it is not an obstacle.
             * 
             * */

            if (_squares[x, y].ContentCode == SquareContent.Others ||
                _squares[x, y].ContentCode == SquareContent.Obstacle)
            {
                return false;
            }
            else
            {
                return true;
            }

            //return _squares[x, y].ContentCode != SquareContent.Obstacle;
        }

        private Point FindCode(int contentIn)
        {
            /*
             * 
             * Find the requested code and return the point.
             * 
             * */
            foreach (Point point in AllSquares())
            {
                if (_squares[point.X, point.Y].ContentCode == contentIn)
                {
                    return new Point(point.X, point.Y);
                }
            }
            return new Point(-1, -1);
        }

        public List<Point> HighlightPath(int vehicleID)
        {
            /*
             * 
             * Mark the path from target to vehicle.
             * 
             * */

            List<Point> result = new List<Point>();
            Point startingPoint = FindCode(SquareContent.FinishLocation(vehicleID));

            result.Add(startingPoint);

            int pointX = startingPoint.X;
            int pointY = startingPoint.Y;
            if (pointX == -1 && pointY == -1)
            {
                return null;
            }

            while (true)
            {
                /*
                 * 
                 * Look through each direction and find the square
                 * with the lowest number of steps marked.
                 * 
                 * */
                Point lowestPoint = Point.Empty;
                int lowest = Pathfinder.bigNumber;

                foreach (Point movePoint in ValidMoves(pointX, pointY))
                {
                    int count = _squares[movePoint.X, movePoint.Y].DistanceSteps;
                    if (count < lowest)
                    {
                        lowest = count;
                        lowestPoint.X = movePoint.X;
                        lowestPoint.Y = movePoint.Y;
                    }
                }
                if (lowest != Pathfinder.bigNumber)
                {
                    /*
                     * 
                     * Mark the square as part of the path if it is the lowest
                     * number. Set the current position as the square with
                     * that number of steps.
                     * 
                     * */
                    _squares[lowestPoint.X, lowestPoint.Y].IsPath = true;
                    pointX = lowestPoint.X;
                    pointY = lowestPoint.Y;

                    //result.Add(new Point(pointX, pointY));
                    result.Insert(0, new Point(pointX, pointY));
                }
                else
                {
                    break;
                }

                if (_squares[pointX, pointY].ContentCode == SquareContent.StartLocation(vehicleID))
                {
                    /*
                     * 
                     * We went from monster to hero, so we're finished.
                     * 
                     * */
                    break;
                }
            }

            return result;
        }

        private static IEnumerable<Point> AllSquares()
        {
            /*
             * 
             * Return every point on the board in order.
             * 
             * */
            for (int x = 0; x < Pathfinder.gridSizeX; x++)
            {
                for (int y = 0; y < Pathfinder.gridSizeY; y++)
                {
                    yield return new Point(x, y);
                }
            }
        }

        private IEnumerable<Point> ValidMoves(int x, int y)
        {
            /*
             * 
             * Return each valid square we can move to.
             * 
             * */
            foreach (Point movePoint in _movements)
            {
                int newX = x + movePoint.X;
                int newY = y + movePoint.Y;

                if (ValidCoordinates(newX, newY) &&
                    SquareOpen(newX, newY))
                {
                    yield return new Point(newX, newY);
                }
            }
        }

        public static Point ExtractMovement(Point _from, Point _to)
        {
            return new Point(
                _to.X - _from.X,
                _to.Y - _from.Y);
        }
    }
}
